#include "stdinc.h"
#include "Video.h"
#include "InputMgr.h"
#include "cmd/cvar.h"

#include <GL/glx.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <X11/extensions/xf86vmode.h>
#include <X11/keysym.h>

#define WINDOW_CLASS	"Fili's OGL app"

class CVideoPlatform
{
public:
	Display *display;
	int screen;
	Window win;
	GLXContext ctx;
	XSetWindowAttributes attr;
	XF86VidModeModeInfo deskMode;

	bool doubleBuffered;
};

/* attributes for a single buffered visual in RGBA format with at least
* 4 bits per color and a 16 bit depth buffer */
static int attrListSgl[] = {GLX_RGBA, GLX_RED_SIZE, 4,
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
None};

/* attributes for a double buffered visual in RGBA format with at least
* 4 bits per color and a 16 bit depth buffer */
static int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 4,
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
None };

CVideo::CVideo()
{
	m_platform = new CVideoPlatform;

	memset(m_platform, 0, sizeof(CVideoPlatform));
}

CVideo::~CVideo()
{
}

bool CVideo::Restart()
{
	KillGLWindow();
	return CreateGLWindow();
}

bool CVideo::Suhutdown()
{
	KillGLWindow();
	return false;
}

void CVideo::Flip()
{
	if (wndContext.doubleBuffered)
	{
		glXSwapBuffers(wndContext.display, wndContext.win);
	}
}

bool CVideo::ProcessMsg()
{
	XEvent event;
	/* handle the events in the queue */
	while (XPending(wndContext.display) > 0)
	{
		printf("Event\n");
		XNextEvent(wndContext.display, &event);
		switch (event.type)
		{
//		case Expose:
//			if (event.xexpose.count != 0)
//				break;
//			DrawGLScene();
//			break;
			/* exit in case of a mouse button press */
		case KeyPress:
			if (XLookupKeysym(&event.xkey, 0) == XK_Escape)
			{
				cvar_set_bool(CVAR_QUIT, true);
				return false;
			}
			break;
		case ClientMessage:
			if (*XGetAtomName(wndContext.display, event.xclient.message_type) ==
				*"WM_PROTOCOLS")
			{
				printf("Exiting sanely...\n");
				cvar_set_bool(CVAR_QUIT, true);
				return false;
			}
			break;
		default:
			break;
		}
	}

	return true;
}

bool CVideo::CreateGLWindow()
{
	XVisualInfo *vi;
	Colormap cmap;
	int dpyWidth, dpyHeight;
	int i;
	int glxMajorVersion, glxMinorVersion;
	int vidModeMajorVersion, vidModeMinorVersion;
	XF86VidModeModeInfo **modes;
	int modeNum;
	int bestMode;
	Atom wmDelete;
	Window winDummy;
	unsigned int borderDummy;

	wndContext.fullscreen	= cvar_get_bool(CVAR_VID_FULLSCREEN);
	wndContext.width		= cvar_get_int(CVAR_VID_WIDTH);
	wndContext.height		= cvar_get_int(CVAR_VID_HEIGHT);
	wndContext.bpp			= cvar_get_int(CVAR_VID_BPP);

	/* set best mode to current */
	bestMode = 0;
	/* get a connection */
	wndContext.display = XOpenDisplay(0);

	if (!wndContext.display)
	{
		LOG->Error("Could not open display.");
		return false;
	}
	wndContext.screen = DefaultScreen(wndContext.display);
	XF86VidModeQueryVersion(wndContext.display, &vidModeMajorVersion,
		&vidModeMinorVersion);
	printf("XF86VidModeExtension-Version %d.%d\n", vidModeMajorVersion,
		vidModeMinorVersion);
	XF86VidModeGetAllModeLines(wndContext.display, wndContext.screen, &modeNum, &modes);
	/* save desktop-resolution before switching modes */
	wndContext.deskMode = *modes[0];
	/* look for mode with requested resolution */
	for (i = 0; i < modeNum; i++)
	{
		if ((modes[i]->hdisplay == wndContext.width) && (modes[i]->vdisplay == wndContext.height))
		{
			bestMode = i;
		}
	}
	/* get an appropriate visual */
	vi = glXChooseVisual(wndContext.display, wndContext.screen, attrListDbl);
	if (vi == NULL)
	{
		vi = glXChooseVisual(wndContext.display, wndContext.screen, attrListSgl);
		wndContext.doubleBuffered = False;
		printf("Only Singlebuffered Visual!\n");
	}
	else
	{
		wndContext.doubleBuffered = True;
		printf("Got Doublebuffered Visual!\n");
	}
	glXQueryVersion(wndContext.display, &glxMajorVersion, &glxMinorVersion);
	printf("glX-Version %d.%d\n", glxMajorVersion, glxMinorVersion);
	/* create a GLX context */
	wndContext.ctx = glXCreateContext(wndContext.display, vi, 0, GL_TRUE);
	/* create a color map */
	cmap = XCreateColormap(wndContext.display, RootWindow(wndContext.display, vi->screen),
		vi->visual, AllocNone);
	wndContext.attr.colormap = cmap;
	wndContext.attr.border_pixel = 0;

	if (wndContext.fullscreen)
	{
		XF86VidModeSwitchToMode(wndContext.display, wndContext.screen, modes[bestMode]);
		XF86VidModeSetViewPort(wndContext.display, wndContext.screen, 0, 0);
		dpyWidth = modes[bestMode]->hdisplay;
		dpyHeight = modes[bestMode]->vdisplay;
		printf("Resolution %dx%d\n", dpyWidth, dpyHeight);
		XFree(modes);

		/* create a fullscreen window */
		wndContext.attr.override_redirect = True;
		wndContext.attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask |
			StructureNotifyMask;
		wndContext.win = XCreateWindow(wndContext.display, RootWindow(wndContext.display, vi->screen),
			0, 0, dpyWidth, dpyHeight, 0, vi->depth, InputOutput, vi->visual,
			CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect,
			&wndContext.attr);
		XWarpPointer(wndContext.display, None, wndContext.win, 0, 0, 0, 0, 0, 0);
		XMapRaised(wndContext.display, wndContext.win);
		XGrabKeyboard(wndContext.display, wndContext.win, True, GrabModeAsync,
			GrabModeAsync, CurrentTime);
		XGrabPointer(wndContext.display, wndContext.win, True, ButtonPressMask,
			GrabModeAsync, GrabModeAsync, wndContext.win, None, CurrentTime);
	}
	else
	{
		/* create a window in window mode*/
		wndContext.attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask |
			StructureNotifyMask;
		wndContext.win = XCreateWindow(wndContext.display, RootWindow(wndContext.display, vi->screen),
			0, 0, wndContext.width, wndContext.height, 0, vi->depth, InputOutput, vi->visual,
			CWBorderPixel | CWColormap | CWEventMask, &wndContext.attr);
		/* only set window title and handle wm_delete_events if in windowed mode */
		wmDelete = XInternAtom(wndContext.display, "WM_DELETE_WINDOW", True);
		XSetWMProtocols(wndContext.display, wndContext.win, &wmDelete, 1);
		XSetStandardProperties(wndContext.display, wndContext.win, cvar_get_string(CVAR_WND_TITLE),
			cvar_get_string(CVAR_WND_TITLE), None, NULL, 0, NULL);
		XMapRaised(wndContext.display, wndContext.win);
	}
	/* connect the glx-context to the window */
	glXMakeCurrent(wndContext.display, wndContext.win, wndContext.ctx);
	XGetGeometry(wndContext.display, wndContext.win, &winDummy, &wndContext.x, &wndContext.y,
		&wndContext.width, &wndContext.height, &borderDummy, &wndContext.bpp);
	printf("Depth %d\n", wndContext.bpp);
	if (glXIsDirect(wndContext.display, wndContext.ctx))
		printf("Congrats, you have Direct Rendering!\n");
	else
		printf("Sorry, no Direct Rendering possible!\n");


	glViewport(0, 0, wndContext.width, wndContext.height);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(
		cvar_get_float(CVAR_FOV),
		(GLfloat)cvar_get_int(CVAR_VID_WIDTH)/(GLfloat)cvar_get_int(CVAR_VID_HEIGHT),
		cvar_get_float(CVAR_ZBUF_MIN),
		cvar_get_float(CVAR_ZBUF_MAX));

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	return True;

}

void CVideo::KillGLWindow()
{
	if (wndContext.ctx)
	{
		if (!glXMakeCurrent(wndContext.display, None, NULL))
		{
			printf("Could not release drawing context.\n");
		}
		glXDestroyContext(wndContext.display, wndContext.ctx);
		wndContext.ctx = NULL;
	}
	/* switch back to original desktop resolution if we were in fs */
	if (wndContext.fullscreen)
	{
		XF86VidModeSwitchToMode(wndContext.display, wndContext.screen, &wndContext.deskMode);
		XF86VidModeSetViewPort(wndContext.display, wndContext.screen, 0, 0);
	}

	if (wndContext.display)
	{
		XCloseDisplay(wndContext.display);
	}

}

int TranslateKey(int key)
{
	return 0;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_ACTIVATE:
		{
			if (!HIWORD(wParam))
			{
//				m_platform->active = true;
			}
			else
			{
//				m_platform->active = false;
			}

			return 0;
		}

	case WM_SYSCOMMAND:
		{
			switch (wParam)
			{
			case SC_SCREENSAVE:
			case SC_MONITORPOWER:
			case SC_KEYMENU:
				return 0;
			}
			break;
		}

	case WM_CLOSE:
		{
			PostQuitMessage(0);
			return 0;
		}

	case WM_KEYDOWN:
		{
			INPUTMGR->KeyDown(TranslateKey(wParam));
			return 0;
		}

	case WM_KEYUP:
		{
			INPUTMGR->KeyUp(TranslateKey(wParam));
			return 0;
		}

	case WM_CHAR:
		{
			INPUTMGR->Char(wParam);
			return 0;
		}

	case WM_MOVE:
		{
//			if (!m_platform->fullscreen)
//			{
//				cvar_set_int(CVAR_VID_XPOS, LOWORD(lParam));
//				cvar_set_int(CVAR_VID_YPOS, HIWORD(lParam));
//			}
		}

//	case WM_SIZE:
//		{
//			ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
//			video_updateWindow();
//			return 0;
//		}
	}

	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}